#ifndef _RETRO_GRIDEDITOR
#define _RETRO_GRIDEDITOR

namespace Retro
{
  namespace Creator
  {
    namespace Editor
    {
      enum GridTool
      {
        GridToolNone    = MapToolNone,
        GridToolSelect  = MapToolSelect,
        GridToolPen     = MapToolCustom,
        GridToolStamp,
        GridToolRect,
        GridToolFill,
        GridToolLast    = GridToolFill,
      };

      enum GridNotify
      {
        GridNotifyIndices = MapNotifyUser,
      };

      class GridEditor : public MapEditor
      {
      private:
        struct UndoData
        {
          Geom::Bounds    bounds;
          Data::GridCell* cells;
          UndoData*       next;
        };

        struct UndoState
        {
          UndoData*   data;
          UndoData*   lastData;
          INT         layer;
          MapTool     tool;
          Geom::Size  mapSize;
          INT         mapLayers;
          UndoState   *prev, *next;
        };

        Geom::Size   copySize;
        Geom::Point  copyOffset;

        BOOL  mSnapSelection;
        BOOL  mShowIndices;

        BOOL* mVisitMap;
        INT*  mFillMap;

        Data::GridCell**  mapBuffer;
        INT   mapTileSize;

        UndoState*       pUndo;
        UndoState*       pCurrentUndo;
        Data::GridCell*  pCopy;
        Data::Texture*   tileset;

        Device::Display::Texture* mTilesetRes;
        Device::Display::Font*    mFontRes;

        INL BOOL            doScale()
        {
          return (GetKeyState(VK_SHIFT) >> 1) &&
            mSelectMode != SelectModeDrag;
        }
        INL BOOL            doSnap()
        {
          return !(GetKeyState(VK_MENU) >> 1);
        }

        INT       getDisplayTexture(INT x, INT y, INT layer);
        INT       getSelectedTexture(INT x, INT y);
        VOID      getGridView(Geom::Rect &view);

        VOID      onPrepareDisplay();
        VOID      onInitializeDisplay();
        VOID      onUninitializeDisplay();
        VOID      drawIndices();

        VOID      drawForeground();
        VOID      drawLayer(INT layer);

        VOID      scrollToGridPoint(const Geom::Point& pt);

        VOID      fillSelection(INT texture);
        VOID      usePen();
        VOID      useSelection();
        VOID      useFill();
        VOID      copySelection();
        VOID      pasteSelection();

        VOID      addToFillList(
          INT iX,
          INT iY,
          INT iOldTexture,
          INT& ioFillCount
          );

        VOID      fillMapBuffer();
        VOID      freeMapBuffer();
        VOID      createMapBuffer();
        VOID      removeUndo();
        VOID      addUndo();
        UndoData* addUndoData();
        VOID      applyUndoBuffer(UndoState* state);
        VOID      clearUndo(UndoState* from);
        VOID      updateSize();

      protected:
        VOID      onMouseDown(Event::MouseEvent& iEvent);
        VOID      onMouseUp(Event::MouseEvent& iEvent);
        VOID      onMouseMove(Event::MouseEvent& iEvent);
        VOID      onKeyDown(Event::KeyEvent& iEvent);

        VOID      initialize();
        VOID      initToolbar();
        VOID      onToolbar(ToolBarNotify* iData);

      public:
        INT       getLineSize()
        {
          return mapTileSize;
        }

        INL Data::GridCell** getMapBuffer()
        {
          return mapBuffer;
        }
        INLC INT& getMapTileSize()
        {
          return mapTileSize;
        }

        INL BOOL  getShowIndices()
        {
          return mShowIndices;
        }
        VOID      setShowIndices(BOOL value);

        INLC Geom::Bounds&  getPixelSelection()
        {
          return mSelection;
        }
        VOID      setPixelSelection(const Geom::Bounds& selection);

        Geom::Bounds  getGridSelection();
        VOID      setGridSelection(const Geom::Bounds& selection);

        INT       getGridTexture(
          INT x,
          INT y,
          INT iLayer
        );
        VOID      setGridTexture(
          INT x,
          INT y,
          INT iLayer,
          INT texture
        );

        INL VOID  setTool(GridTool value)
        {
          MapEditor::setTool((MapTool)value);
        }
        VOID      setTool(MapTool value);

        INLC Data::GridCell* getCopy()
        {
          return pCopy;
        }
        INL VOID  clearCopy()
        {
          delete[] pCopy;
          pCopy = NULL;
        }
        INL VOID  clearUndo()
        {
          clearUndo(pUndo);
          pUndo = pCurrentUndo = NULL;
        }

        INL BOOL  canUndo()
        {
          return pCurrentUndo != NULL;
        }
        INL BOOL  canRedo()
        {
          if (pCurrentUndo)
            return pCurrentUndo->next != NULL;

          return pUndo != NULL;
        }

        BOOL undo();
        BOOL redo();
        BOOL save(HANDLE iFile);
        VOID flush(Data::Resource* oResource);
        VOID setBufferSize(INT cx, INT cy, INT layers);
        VOID setTileset(INT iTilesetID);

        GridEditor();
      };
    } // namespace Editor
  } // namespace Creator
} // namespace Retro

#endif